package dao

import (
	"context"
	"errors"
	"github.com/go-sql-driver/mysql"
	"gorm.io/gorm"
	"time"
)

var (
	ErrUserDuplicate       = errors.New("duplicate")
	ErrUserEmailNotExists  = errors.New("email not exists")
	ErrUserPhoneNotExists  = errors.New("phone not exists")
	ErrUserOpenIDNotExists = errors.New("openID not exists")
)

type UserDAO interface {
	Insert(ctx context.Context, user User) error
	FindByEmail(ctx context.Context, email string) (User, error)
	UpdateById(ctx context.Context, user User) error
	FindById(ctx context.Context, id int64) (User, error)
	FindByPhone(ctx context.Context, phone string) (User, error)
	FindByOpenID(ctx context.Context, id string) (User, error)
}

type GORMUserDAO struct {
	db *gorm.DB
}

func NewUserDAO(db *gorm.DB) UserDAO {
	return &GORMUserDAO{
		db: db,
	}
}

func (u *GORMUserDAO) Insert(ctx context.Context, user User) error {
	milli := time.Now().UnixMilli()
	user.Ctime = milli
	user.Utime = milli
	err := u.db.WithContext(ctx).Create(&user).Error
	// 特殊处理唯一约束错误
	if mError, ok := err.(*mysql.MySQLError); ok {
		const uniqueIndexConflictError = 1062
		if mError.Number == uniqueIndexConflictError {
			return ErrUserDuplicate
		}
	}
	if err != nil {
		return err
	}
	return nil
}

func (u *GORMUserDAO) FindByEmail(ctx context.Context, email string) (User, error) {
	var user User
	err := u.db.WithContext(ctx).Where("email = ?", email).Find(&user).Error
	if err != nil {
		return User{}, ErrUserEmailNotExists
	}
	return user, nil
}

func (u *GORMUserDAO) UpdateById(ctx context.Context, user User) error {
	return u.db.WithContext(ctx).
		Model(&User{}).
		Where("id = ?", user.Id).
		Update("nickname", user.Nickname).
		Update("birthday", user.Birthday).
		Update("utime", time.Now().UnixMilli()).
		Update("aboutme", user.Aboutme).Error
}

func (u *GORMUserDAO) FindById(ctx context.Context, id int64) (User, error) {
	var dbUser User
	err := u.db.WithContext(ctx).Where("id = ?", id).First(&dbUser).Error
	if err != nil {
		return User{}, err
	}
	return dbUser, nil
}

func (u *GORMUserDAO) FindByPhone(ctx context.Context, phone string) (User, error) {
	var user User
	err := u.db.WithContext(ctx).Where("phone = ?", phone).First(&user).Error
	if err != nil {
		return User{}, ErrUserPhoneNotExists
	}
	return user, nil
}

func (u *GORMUserDAO) FindByOpenID(ctx context.Context, openID string) (User, error) {
	var user User
	err := u.db.WithContext(ctx).Where("wechat_open_id = ?", openID).First(&user).Error
	if err != nil {
		return User{}, ErrUserOpenIDNotExists
	}
	return user, nil
}

// User 对应数据库中的user表
type User struct {
	Id       int64  `gorm:"primaryKey,autoIncrement"`
	Email    string `gorm:"unique"`
	Phone    string `gorm:"unique"`
	Password string
	Nickname string `gorm:"type=varchar(128)"`
	// YYYY-MM-DD
	Birthday int64
	Aboutme  string `gorm:"type=varchar(4096)"`

	// 时区，UTC 0 的毫秒数
	// 创建时间
	Ctime int64
	// 更新时间
	Utime int64

	// json 存储
	//Addr string

	WeChatOpenID   string `gorm:"unique"`
	WeChatUniqueID string `gorm:"unique"`
}
